package org.ccay.web.support.internal.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.inject.Named;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.ccay.core.exception.impl.CommonSystemException;
import org.ccay.core.ioc.Ccay;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.request.Application;
import org.ccay.core.request.impl.RequestContext;
import org.ccay.core.util.CollectionUtil;
import org.ccay.core.util.EncodingUtil;
import org.ccay.core.util.JsonUtil;
import org.ccay.core.util.StreamUtil;
import org.ccay.core.util.StringUtil;
import org.ccay.registry.util.RegistryQueryUtil;
import org.ccay.web.support.ThemeBean;
import org.ccay.web.support.WebRequestVO;
import org.ccay.web.support.WorkspaceVO;
import org.ccay.web.support.filter.ResponseUtil;
import org.ccay.web.support.internal.IResourceHandler;
import org.ccay.web.support.service.IHomeService;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * WEB资源处理器
 * 
 * 
 * @since 2012-3-26
 */
@Named("webResourceHandler")
public final class WebResourceHandler implements IResourceHandler {

	private static Set<String> uriLoadByTemplate = null;
	// 访问黑名单
	private static Set<String> uriBlackList = genBlackUriSet();

	// 日志
	private static final ILogger logger = CcayLoggerFactory.getLogger(WebResourceHandler.class);

	/**
	 * @return
	 */
	private static Set<String> genTemplateUriSet() {
		Set<String> uriSet = new HashSet<String>();
		uriSet.add("/");
		uriSet.add("/index.html");
		uriSet.add("/index_dev.html");
		uriSet.add("/new.html");
		uriSet.add("/har/maintenance.html");
		String beanName = "uriByTemplate";
		if (!Ccay.getContext().containsBean(beanName)) {
			return uriSet;
		}
		Set<String> additional = (Set<String>) Ccay.getContext().getBean(beanName, Set.class);
		if (!CollectionUtil.isNullOrEmpty(additional)) {
			uriSet.addAll(additional);
		}
		return uriSet;
	}

	/**
	 * @return
	 */
	private static Set<String> genBlackUriSet() {
		Set<String> blackList = new HashSet<String>();
		blackList.add("/index.html");
		blackList.add("/index_dev.html");
		return blackList;
	}

	public void sendResourceToResponse(HttpServletRequest request, HttpServletResponse response, WebRequestVO webRequest)
			throws IOException {
		if (uriLoadByTemplate == null) {
			uriLoadByTemplate = genTemplateUriSet();
		}
		if (uriLoadByTemplate.contains(webRequest.getShortUri())) {
			outputByTemplateEngine(request, response, webRequest.getFilePath());
			webRequest.setHandled(true);
			return;
		}
		// 尝试从Classpath加载
		if (!loadFromClasspath(response, webRequest)) {
			throw new FileNotFoundException("Resource " + webRequest.getFilePath() + " not found.");
		}

		webRequest.setHandled(true);
	}

	/**
	 * 
	 * @param response
	 * @param fileUri
	 * @throws IOException
	 * @throws FileNotFoundException
	 */
	private boolean loadFromClasspath(HttpServletResponse response, WebRequestVO webRequest) throws IOException,
			FileNotFoundException {
		URL resource = this.getClass().getClassLoader().getResource(webRequest.getFilePath());
		// 如果存在该文件，便输出该文件
		if (resource == null) {
			return false;
		}

		if (ResponseUtil.process304(response, webRequest, getServerModifiedTime(resource))) {
			return true;
		}
		InputStream inputStream =null;
		try{
			inputStream = resource.openStream();
			return true;
		}finally {
			// transfer中会关闭IO流
			StreamUtil.transfer(inputStream, response.getOutputStream());
		}
	}

	private static Map<String, String> cachedResources = new ConcurrentHashMap<String, String>();

	/**
	 * 通过模板引擎输出页面
	 * 
	 * @param httpResponse
	 * @param filePath
	 * @throws IOException
	 */
	private void outputByTemplateEngine(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
			String filePath) throws IOException {

		// 获得当前主题
		ThemeBean currentTheme = getCurrentTheme(httpRequest);
		// 主页路径
		String path = "web/" + currentTheme.getHomeUrl();
		// 访问黑名单:可直接url指定访问uriLoadByTemplate内的文件，但
		// "/index.html","/index_dev.html"除外
		if (!uriBlackList.contains(filePath.substring(3))) {
			path = filePath;
		}
		// 开发资源调试标识:若请求url指定?debug为任意,则都不读取缓存
		boolean isDebug = httpRequest.getParameter("debug") != null;
		boolean noMenu = httpRequest.getParameter("nomenu") != null;
		boolean noMenuOnly = httpRequest.getParameter("nomenuonly") != null;
		// 存放框架的js和css资源
		Map<String, List<?>> resource = new HashMap<String, List<?>>();
		
		Map<String, Object> bindItems = new HashMap<String, Object>();
		
		// 模板中可使用的变量“env”,“resource”,"noMenu","appVersion"
		bindItems.put("env", RequestUtil.getEnvBeanByNetwrok());
		bindItems.put("resource", resource);
		bindItems.put("noMenu", noMenu);
		bindItems.put("noMenuOnly", noMenuOnly);
		bindItems.put("enableHomeCache", RegistryQueryUtil.getBooleanValue("Ccay.GlobalParamters.EnableHomeCache"));
		// 以数据字典配置的版本号为主,默认是web.support.beans.xml配置的ccayAppVersion的值
		RegistryQueryUtil.getValue("Ccay.GlobalParamters.AppVersion");
//		bindItems.put("appVersion", GlobalParameters.get("Ccay.GlobalParamters.AppVersion", (String)Ccay.getContext().getBean("ccayAppVersion", String.class)));
		bindItems.put("appVersion", RegistryQueryUtil.getValue("Ccay.GlobalParamters.AppVersion"));
		
		//允许在数据字典中配置自定义参数，参数配置在GlobalParameters下，参数之间以英文逗号分隔，并且配置系统已存在的变量将无效，如:env\resource等。 lwx324861 2016-08-31
		String customParamStr = RegistryQueryUtil.getValue("Ccay.GlobalParamters.CustomParamters");
		if(!StringUtil.isNullOrEmpty(customParamStr)){
			String[] customParamters= customParamStr.split(",");
			for(String str : customParamters){
				if(!StringUtil.isNullOrEmpty(str) && !bindItems.containsKey(str)){
					bindItems.put(str, httpRequest.getParameter(str) != null);
				}
			}
		}

		if (isDebug) {
			// 主题配置的js资源
			resource.put("jsUrlList", (List<String>) Ccay.getContext().getBean("ccayJsListDev", List.class));
			resource.put("cssUrlList", currentTheme.getCssUrlList());

			genTemplate(httpResponse, path, resource, bindItems);
			
			return ;
		}
		// 缓存key
		String cacheKeyMenu = null;
		if(noMenu){
			cacheKeyMenu = "true";
		}else if(noMenuOnly){
			cacheKeyMenu = "false";
		}
		String cacheKey = currentTheme.getThemeName() + ":" + path + ":" + cacheKeyMenu;
		// 读取缓存
		String resourceString = cachedResources.get(cacheKey);

		// 若缓存中存在文件,则缓存文件生效
		if (!StringUtil.isNullOrEmpty(resourceString)) {
			httpResponse.getWriter().write(writeEnvInfo(resourceString));
			return;
		}
		// 主题配置的js资源
		resource.put("jsUrlList", currentTheme.getJsUrlList());
		resource.put("cssUrlList", currentTheme.getCssUrlList());

		// 处理模板
		resourceString = genTemplate(httpResponse, path, resource, bindItems);

		// 非开发非调试环境缓存
		if (!"dev".equals(Application.getCurrent().getEnviorment())) {
			cachedResources.put(cacheKey, resourceString);
		}
	}
	
	/** 
	 * 写入个性化环境信息
	 * @param writer
	 */
	private String writeEnvInfo(String resourceString) {
		if (RegistryQueryUtil.getBooleanValue("Ccay.GlobalParamters.EnableHomeCache")) {
			return resourceString;
		}
		StringBuffer sb = new StringBuffer();
		sb.append("<script type=\"text/javascript\">");
		
		IHomeService workspaceService =(IHomeService)Ccay.getContext().getBean("homeService");
		try {
			//加载个性化数据
			WorkspaceVO wvo = workspaceService.findWorkspaceInfo(false);
			wvo.setNetwork(RequestContext.getCurrent().getApplication().getNetwork());
			wvo.getEnvBean().setAppName(RequestContext.getCurrent().getApplication().getAppName());
			
			//TODO 加载i18n数据
			/*II18nQueryService i18nService = (II18nQueryService)Ccay.getContext().getBean("i18nQueryService");;
			//查询数据		
			List<I18nVO> i18ns = i18nService.findI18nList(null,  RequestContext.getCurrent().getUserLanguage());
			//迭代输出资源数据
			Map<String, String> i18n = new HashMap<String, String>();
			for (I18nVO i18nvo : i18ns) {
				if (i18nvo.getI18nKey() != null){
					i18n.put(i18nvo.getI18nKey().trim(), i18nvo.getI18nContent());
				}
			}
			
			wvo.setI18n(i18n);
			*/
			//转义输出
			sb.append("var workspaceVO=" + JsonUtil.objectToJson(wvo).replace(">", "\\>"));
			
		} catch (Exception e) {
			logger.error(e);
		}
		
		sb.append("</script>");
		return resourceString.replace("<body>", "<body>" + sb.toString());
	}
	
	/**
	 * 处理模板，并返回字符串流
	 */
	private String genTemplate(HttpServletResponse httpResponse, String path, Map<String, List<?>> resource, Map<String, Object> bindItems)
			throws IOException {
		InputStream resourceStream = null;
		InputStreamReader streamReader = null;
		try {
			resourceStream = this.getClass().getClassLoader().getResourceAsStream(path);
			streamReader = new InputStreamReader(resourceStream,EncodingUtil.UTF_8);
			String resourceString = FreeMarkerTemplateUtils.processTemplateIntoString(new Template("", streamReader,
					new Configuration(), EncodingUtil.UTF_8), bindItems);
			httpResponse.getWriter().write(writeEnvInfo(resourceString));
			return resourceString;
		} catch (TemplateException e) {
			throw new CommonSystemException("Convert home page error.", e);
		} finally {
			StreamUtil.closeStreams(resourceStream, streamReader);
		}
	}
	/**
	 * 获取文件的服务器修改时间
	 */
	private long getServerModifiedTime(URL resource) {
		String filePath = resource.getFile();

		int sIndex = filePath.indexOf("!");
		if (sIndex >= 0) {
			filePath = filePath.substring(0, sIndex);
		}

		if (filePath.startsWith("file:")) {
			filePath = filePath.substring("file:".length());
		}

		return new File(filePath).lastModified();
	}

	public void clearResourceCache() {
		uriLoadByTemplate = genTemplateUriSet();
		cachedResources.clear();
	}

	/**
	 * 获得当前的主题
	 * 
	 * 
	 * @since 2013-7-28
	 * @return
	 */
	private ThemeBean getCurrentTheme(HttpServletRequest httpRequest) {
		// 获得session中的主题
		String themeName = (String) SecurityUtils.getSubject().getSession(true).getAttribute("THEME");
		// 获得主题
		//ThemeBean currentTheme = env.getThemes().get(themeName);
		Cookie[] cookies  = httpRequest.getCookies();
		if(cookies !=null){
			for(Cookie cookie:cookies){
				if(cookie.getName().equals("THEME")){
					themeName = cookie.getValue();
					break;
				}
			}
		}
		
		ThemeBean currentTheme = null;
		try{
			currentTheme = Ccay.getContext().getBean(themeName, ThemeBean.class);
		}catch(Exception ex){
			logger.warn(ex.getMessage());
		}

		// 若主题不存在,则取主题'ccayDefaultTheme'，确保是存在的
		if (currentTheme == null) {
			logger.warn("set theme error,the theme : '" + themeName
					+ "' not exist, and now set the theme name as 'ccayDefaultTheme'.");
			themeName = "ccayDefaultTheme";
			//currentTheme = env.getThemes().get(themeName);
			currentTheme = Ccay.getContext().getBean(themeName, ThemeBean.class);
			//currentTheme = Ccay.getContext().getBean("ccay", ThemeBean.class);
		}
		List<String> ccayCssList = currentTheme.getCssUrlList();
		// 判断是否配置主页homeUrl,否则使用框架默认主题的主页
		if (StringUtil.isNullOrEmpty(currentTheme.getHomeUrl())) {
			currentTheme.setHomeUrl(RequestUtil.getEnvBeanByNetwrok().getHomePath());
		}
		return currentTheme;
	}	
}
